package cs4game;

import java.util.ArrayList;
import boots.boots.*;
import java.util.HashSet;
import java.util.Random;
import java.util.Set;

import org.lwjgl.opengl.Display;
import org.lwjgl.opengl.DisplayMode;
import org.lwjgl.opengl.GL11;
import org.lwjgl.opengl.GL12;
import org.lwjgl.util.glu.GLU;

@SuppressWarnings("unused")
public class Floor extends TerrainEntity {

	private ArrayList<Vector3D> corners;
	private ArrayList<Vector3D[]> subRects = new ArrayList<Vector3D[]>();
	private float leftBound, rightBound, topBound, bottomBound;
	private double ranSeed = Math.random();
	private double tileSize;
	int[][] texCoors;
	Style style;

	public Floor(ArrayList<Vector3D> cs, Style s){
		style = s;
		s.loadTexture();
		corners = cs;
		subSplit();
		tileSize=1;
		Random ran = new Random();
		ran.setSeed((long) ranSeed);
		int tileDimX = (int)Math.ceil((rightBound-leftBound)/tileSize);
		int tileDimZ = (int)Math.ceil((topBound-bottomBound)/tileSize);
		texCoors = new int[tileDimX][tileDimZ];
		int numFloorTiles = style.getNumFloors();
		for(int i = 0; i<tileDimX; i++)
			for(int j = 0; j<tileDimZ; j++){
				//System.out.println("numfloortilez "+numFloorTiles);
				texCoors[i][j] = ran.nextInt(numFloorTiles);
			}
	}

	public void draw(Vector3D offset){
		//if(room.isinfinite)
			//fail(0);
		GL11.glBegin(GL11.GL_QUADS);
		for(Vector3D[] pts: subRects){
			Vector3D subBL = pts[0]; //System.out.println("SUBBL: "+pts[0]);//subrect bottom left
			Vector3D subBR = pts[1]; //System.out.println("SUBBR: "+pts[1]);
			Vector3D subTR = pts[2]; //System.out.println("SUBTR: "+pts[2]); //subrect top right 
			Vector3D subTL = pts[3]; //System.out.println("SUBTL: "+pts[3]);
			
			double bottomLeftover = Math.round(1000.0*(subBL.z%tileSize)/tileSize)/1000.0;
			double leftLeftover = Math.round(1000.0*(subBL.x%tileSize)/tileSize)/1000.0;
			double topLeftover = Math.round(1000.0*(subTR.z%tileSize)/tileSize)/1000.0;
			double rightLeftover = Math.round(1000.0*(subTR.x%tileSize)/tileSize)/1000.0;
			if(leftLeftover!=0.66)
				continue;
			System.out.println("These are gonna be numberz "+subBL+" "+subBR+" "+subTR+" "+subTL+" <-- Those were numbers");
			System.out.println("These are also numbersz "+bottomLeftover+" "+leftLeftover+" "+topLeftover+" "+rightLeftover);
			

			//Bottom left corner
			TexPoint[] blTileTexPts = style.getFloorTexCoords(getTileAt(subBL));
			drawPortion(blTileTexPts, new Vector3D(subBL.x, subBL.y, subBL.z+tileSize*bottomLeftover), new Vector3D(subBL.x+tileSize*leftLeftover, subBL.y, subBL.z),
					leftLeftover, 1, bottomLeftover, 1);
			//Top left corner
			TexPoint[] tlTileTexPts = style.getFloorTexCoords(getTileAt(subTL));
			drawPortion(tlTileTexPts, new Vector3D(subTL.x, subTL.y, subTL.z), new Vector3D(subTL.x+tileSize*leftLeftover, subTL.y, subTL.z-tileSize*topLeftover),
					leftLeftover, 1, 1, topLeftover);
			//Top right corner
			TexPoint[] trTileTexPts = style.getFloorTexCoords(getTileAt(subTR));
			drawPortion(trTileTexPts, new Vector3D(subTR.x-tileSize*rightLeftover, subTR.y, subTR.z), new Vector3D(subTR.x, subTR.y, subTR.z-tileSize*topLeftover),
					1, rightLeftover, 1, topLeftover);
			//Bottom right corner
			TexPoint[] brTileTexPts = style.getFloorTexCoords(getTileAt(subBR));
			drawPortion(brTileTexPts, new Vector3D(subBR.x-tileSize*rightLeftover, subBR.y, subBR.z+tileSize*bottomLeftover), new Vector3D(subBR.x, subBR.y, subBR.z),
					1, rightLeftover, bottomLeftover, 1);
			//System.out.println("limit: "+(subTL.z-topLeftover));
			//System.out.println("least: "+ (subBL.z+bottomLeftover));
			//Left side
			for(double i = (subBL.z+bottomLeftover); i<(subTL.z-topLeftover); i+=tileSize){
				//System.out.print("i: "+i);
				Vector3D thisTileBL = new Vector3D(subBL.x, 0, i);
				TexPoint[] leftTexPts = style.getFloorTexCoords(getTileAt(thisTileBL));
				drawPortion(leftTexPts, new Vector3D(thisTileBL.x, 0, thisTileBL.z+tileSize), new Vector3D(thisTileBL.x+tileSize*leftLeftover, 0, thisTileBL.z),
						leftLeftover, 1, 1, 1);
			}
			//Top side
			for(double i = (subTL.x+leftLeftover); i<(subTR.x-rightLeftover); i+=tileSize){
				//System.out.print("i: "+i);
				Vector3D thisTileBL = new Vector3D(i, 0, subTL.z-tileSize*topLeftover);
				TexPoint[] topTexPts = style.getFloorTexCoords(getTileAt(thisTileBL));
				drawPortion(topTexPts, new Vector3D(thisTileBL.x, 0, thisTileBL.z+tileSize*topLeftover), new Vector3D(thisTileBL.x+tileSize, 0, thisTileBL.z),
						1, 1, 1, topLeftover);
			}
			//Right side
			for(double i = (subBR.z+bottomLeftover); i<(subTR.z-topLeftover); i+=tileSize){
				//System.out.print("i: "+i);
				Vector3D thisTileBL = new Vector3D(subBR.x-tileSize*rightLeftover, 0, i);
				TexPoint[] rightTexPts = style.getFloorTexCoords(getTileAt(thisTileBL));
				drawPortion(rightTexPts, new Vector3D(thisTileBL.x, 0, thisTileBL.z+tileSize), new Vector3D(thisTileBL.x+tileSize*rightLeftover, 0, thisTileBL.z),
						1, rightLeftover, 1, 1);
			}
			//Bottom side
			for(double i = (subBL.x+leftLeftover); i<(subBR.x-rightLeftover); i+=tileSize){
				//System.out.print("i: "+i);
				Vector3D thisTileBL = new Vector3D(i, 0, subBL.z);
				TexPoint[] leftTexPts = style.getFloorTexCoords(getTileAt(thisTileBL));
				drawPortion(leftTexPts, new Vector3D(thisTileBL.x, 0, thisTileBL.z+tileSize*bottomLeftover), new Vector3D(thisTileBL.x+tileSize, 0, thisTileBL.z),
						1, 1, bottomLeftover, 1);
			}
			//Center
			for(double i = (subBL.z+bottomLeftover); i<(subTL.z-topLeftover); i+=tileSize){
				for(double j = (subTL.x+leftLeftover); j<(subTR.x-rightLeftover); j+=tileSize){
					Vector3D thisTileBL = new Vector3D(j, 0, i);
					TexPoint[] centerTexPts = style.getFloorTexCoords(getTileAt(thisTileBL));
					drawPortion(centerTexPts, new Vector3D(thisTileBL.x, 0, thisTileBL.z+tileSize), new Vector3D(thisTileBL.x+tileSize, 0, thisTileBL.z),
							1, 1, 1, 1);
				}
			}

		}
		
		GL11.glEnd();
	}

	public void drawPortion(TexPoint[] tileTexPts, Vector3D topLeft, Vector3D bottomRight, double xLeftPortion, double xRightPortion, double yBottomPortion, double yTopPortion){
		//Topleft corner
		GL11.glTexCoord2d(tileTexPts[0].x+style.getTileSize()*(1-xLeftPortion), tileTexPts[0].y+style.getTileSize()*(1-yTopPortion));
		//GL11.glTexCoord2d(0,0);
		GL11.glVertex3d(topLeft.x, topLeft.y, topLeft.z);
		//Topright corner
		GL11.glTexCoord2d(tileTexPts[1].x-style.getTileSize()*(1-xRightPortion), tileTexPts[0].y+style.getTileSize()*(1-yTopPortion));
		//GL11.glTexCoord2d(0.25,0);
		GL11.glVertex3d(bottomRight.x, topLeft.y, topLeft.z);
		//bottomRight corner
		GL11.glTexCoord2d(tileTexPts[1].x-style.getTileSize()*(1-xRightPortion), tileTexPts[1].y-style.getTileSize()*(1-yBottomPortion));
		//GL11.glTexCoord2d(0.25,0.25);
		GL11.glVertex3d(bottomRight.x, bottomRight.y, bottomRight.z);
		//bottomleft corner
		GL11.glTexCoord2d(tileTexPts[0].x+style.getTileSize()*(1-xLeftPortion), tileTexPts[1].y-style.getTileSize()*(1-yBottomPortion));
		//GL11.glTexCoord2d(0,0.25);
		GL11.glVertex3d(topLeft.x, bottomRight.y, bottomRight.z);
	}

	public int getTileAt(Vector3D p){
		int x = (int) ((p.x-leftBound)/tileSize);
		int z = (int) ((p.z-bottomBound)/tileSize);
		if(x>=texCoors.length)
			x--;
		if(z>=texCoors[x].length)
			z--;
		return texCoors[x][z];
	}

	//Ultimate goal of this function is to split any given floor defined by its corners into a set of rectangles so it can be drawn to the screen.
	//...yeah, it's really long and probably inefficient as heck, but if you don't like it... write a better one!
	//(just make sure it works before you replace this completely)
	@SuppressWarnings("unchecked")
	public void subSplit(){
		ArrayList<Vector3D> cornerVertCopy = (ArrayList<Vector3D>) corners.clone();
		ArrayList<Vector3D> cornerHoriCopy = (ArrayList<Vector3D>) corners.clone();
		ArrayList<ArrayList<Vector3D>> vertMap = new ArrayList<ArrayList<Vector3D>>();
		ArrayList<ArrayList<Vector3D>> horiMap = new ArrayList<ArrayList<Vector3D>>();
		double minHoriNum = Double.MAX_VALUE;
		double minVertNum =  Double.MAX_VALUE;
		Set<Vector3D> minHoriPoints = new HashSet<Vector3D>();
		Set<Vector3D> minVertPoints = new HashSet<Vector3D>();

		int hI = 0;
		while(!cornerHoriCopy.isEmpty()){
			for(int i = 0; i<cornerHoriCopy.size(); i++){
				if(cornerHoriCopy.get(i).x<minHoriNum){
					minHoriNum = cornerHoriCopy.get(i).x;
					minHoriPoints.clear();
				}
				if(cornerHoriCopy.get(i).x==minHoriNum)
					minHoriPoints.add(cornerHoriCopy.get(i));	
			}
			int count = 0;
			horiMap.add(new ArrayList<Vector3D>());
			for(Vector3D p: minHoriPoints){
				count++;
				horiMap.get(hI).add(p);
				cornerHoriCopy.remove(p);
			}
			if(count<2){
				System.out.println("Bad definintion!");
				return;
			}

			minHoriPoints.clear();
			hI++;
			minHoriNum = 999999999;
		}

		int vI = 0;
		while(!cornerVertCopy.isEmpty()){
			for(int i = 0; i<cornerVertCopy.size(); i++){
				if(cornerVertCopy.get(i).z<minVertNum){
					minVertNum = cornerVertCopy.get(i).z;
					minVertPoints.clear();
				}
				if(cornerVertCopy.get(i).z==minVertNum)
					minVertPoints.add(cornerVertCopy.get(i));	
			}
			int count = 0;
			vertMap.add(new ArrayList<Vector3D>());
			for(Vector3D p: minVertPoints){
				count++;
				vertMap.get(vI).add(p);
				cornerVertCopy.remove(p);
			}
			if(count<2){
				System.out.println("Bad definintion!");
				return;
			}

			minVertPoints.clear();
			vI++;
			minVertNum = 999999999;
		}

		bottomBound = vertMap.get(0).get(0).z;
		leftBound = horiMap.get(0).get(0).x;
		topBound = vertMap.get(vertMap.size()-1).get(0).z;
		rightBound = horiMap.get(horiMap.size()-1).get(0).x;

		//Stand back guys, I'm gonna try a BUBBLE SORT!!!! (But seriously, these have so few elements efficiency will be a nonissue)
		for(int i = 0; i<vertMap.size(); i++){
			ArrayList<Vector3D> sortedVM = vertMap.get(i);
			for(int j = 0; j<sortedVM.size(); j++){
				for(int k = 1; k<sortedVM.size()-j; k++){
					if(sortedVM.get(k).x<sortedVM.get(k-1).x){
						Vector3D temp = sortedVM.get(k);
						sortedVM.set(k, sortedVM.get(k-1));
						sortedVM.set(k-1, temp);
					}
				}
			}
			vertMap.set(i, sortedVM);
		}

		for(int i = 0; i<vertMap.size()-1; i++){
			while(!vertMap.get(i).isEmpty()){
				//System.out.println(i);
				Vector3D p1 = vertMap.get(i).get(0);
				Vector3D p2 = vertMap.get(i).get(1);
				vertMap.get(i).remove(p1);
				vertMap.get(i).remove(p2);
				double rectZMax = vertMap.get(i+1).get(0).z;
				Vector3D p3 = new Vector3D(p2.x, 0, rectZMax);
				Vector3D p4 = new Vector3D(p1.x, 0, rectZMax);
				boolean rmp3 = false, rmp4 = false;
				//System.out.println("0 "+vertMap.get(i+1).size());
				for(int j = vertMap.get(i+1).size()-1; j>=0; j--){
					if(p3.x==vertMap.get(i+1).get(j).x){
						vertMap.get(i+1).remove(j);
						rmp3 = true;
					}
					else if(p4.x==vertMap.get(i+1).get(j).x){
						vertMap.get(i+1).remove(j);
						rmp4 = true;
					}
				}
				//System.out.println("1 "+vertMap.get(i+1).size());
				if(!rmp3 || !rmp4)
					for(int j = 0; j<vertMap.get(i+1).size(); j++){
						if(!rmp3 && vertMap.get(i+1).get(j).x>p3.x){
							rmp3 = true;
							vertMap.get(i+1).add(j, p3);
						}
						if(!rmp4 && vertMap.get(i+1).get(j).x>p4.x){
							rmp4 = true;
							vertMap.get(i+1).add(j, p4);
						}
					}
				//System.out.println("2 "+vertMap.get(i+1).size());
				if(!rmp3)
					vertMap.get(i+1).add(p3);
				if(!rmp4)
					vertMap.get(i+1).add(p4);
				//System.out.println("3 "+vertMap.get(i+1).size());
				subRects.add(new Vector3D[]{p1, p2, p3, p4});
				//System.out.println("["+subRects.get(subRects.size()-1)[0]+"; "+subRects.get(subRects.size()-1)[1]+"; "+subRects.get(subRects.size()-1)[2]+"; "+subRects.get(subRects.size()-1)[3]+"]");
			}
		}

		for(int i = 0; i<subRects.size(); i++){
			System.out.println("["+subRects.get(i)[0]+"; "+subRects.get(i)[1]+"; "+subRects.get(i)[2]+"; "+subRects.get(i)[3]+"]");
		}

	}


	public static void main(String[] args){
		Vector3D[] c = new Vector3D[]{
				new Vector3D(0, 0, 3.5),
				new Vector3D(0, 0, 5.5),
				new Vector3D(7.5, 0, 5.5),
				new Vector3D(7.5, 0, 3.5),
				new Vector3D(6, 0, 3.5),
				new Vector3D(6, 0, 0.5),
				new Vector3D(5, 0, 0.5),
				new Vector3D(5, 0, 2),
				new Vector3D(2.66, 0, 2),
				new Vector3D(2.66, 0, 3.5)
		};
		ArrayList<Vector3D> cs = new ArrayList<Vector3D>();
		for(Vector3D p: c)
			cs.add(p);
		Style.buildStyleMap("./xml/styles.xml");
		

		try{
			Display.setDisplayMode(new DisplayMode(1000,1000)); //Sets size of the screen (like a JFrame)
			Display.create(); //Physically creates the frame
			Display.setTitle("Working Title"); //title shown at top
		}catch(Exception ex){
			ex.printStackTrace();
			System.exit(0);
		}
		float[] eye = new float[]{5f, -15f, 5f}; //Places the camera at this (arbitrary) point
		float[] center = new float[]{5f, 0f, 5f}; //Focus the camera (in this case, directly behind);
		float[] up = new float[]{0.0f,0.0f,1.0f}; //Up is directly up
		Camera cam = new Camera(new MainStage(), eye, center, up);
		
		GL11.glEnable(GL11.GL_DEPTH_TEST);
		GL11.glEnable(GL11.GL_TEXTURE_2D); 
		//GL11.glDisable(GL11.GL_BLEND);

		GL11.glTexEnvi(GL11.GL_TEXTURE_ENV, GL11.GL_TEXTURE_ENV_MODE, GL11.GL_DECAL);
		GL11.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_WRAP_S, GL12.GL_CLAMP_TO_EDGE);
		GL11.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_WRAP_T, GL12.GL_CLAMP_TO_EDGE);
		GL11.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MIN_FILTER, GL11.GL_NEAREST);
		GL11.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MAG_FILTER, GL11.GL_NEAREST);
		GL11.glMatrixMode(GL11.GL_PROJECTION);
		GLU.gluPerspective( /* field of view in degree */ 40.0f,
				/* aspect ratio */ 1.0f,
				/* Z near */ 0.01f, /* Z far */ 100.0f);

		GL11.glDisable(GL11.GL_SMOOTH);
		
		Floor f = new Floor(cs, Style.styleMap.get("ship1"));
		while(!Display.isCloseRequested()){ //Waits until the [X] button is pressed //&& !escapeKey){
			GL11.glClear(GL11.GL_COLOR_BUFFER_BIT | GL11.GL_DEPTH_BUFFER_BIT);
			f.draw(new Vector3D(0, 0, 0)); //Internal function -- draw everything!
			GL11.glFlush(); //Forces OpenGL to run through its drawing buffer immediately
			Display.sync(4); //Limits framerate to 60FPS
			
			Display.update(); //Like repaint
		}
	}

}
